﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.Hosting;
using System.Xml;
using System.Xml.Serialization;
using Mozlite.Cryptography;
using Mozlite.Properties;
using System.Web.Configuration;

namespace Mozlite
{
    /// <summary>
    /// 通用辅助方法。
    /// </summary>
    public class Cores
    {
        #region initializers
        /// <summary>
        /// 初始化类<see cref="T:Mozlite.CommonHelper"/>。
        /// </summary>
        protected Cores() { }

        static Cores()
        {
            SecurityPermission permission = new SecurityPermission(SecurityPermissionFlag.SerializationFormatter);
            try
            {
                permission.Demand();
                CanBinarySerialize = true;
            }
            catch (SecurityException)
            {
                CanBinarySerialize = false;
            }
            try
            {
                ApplicationPath = HttpRuntime.AppDomainAppPath;
                ApplicationUrl = HttpRuntime.AppDomainAppVirtualPath;
            }
            catch
            {
                ApplicationUrl = "/";
            }
        }
        /// <summary>
        /// 是否能二进制序列化。
        /// </summary>
        public static bool CanBinarySerialize { get; private set; }
        #endregion

        #region path helpers
        /// <summary>
        /// 应用程序物理地址。
        /// </summary>
        public static string ApplicationPath { get; private set; }
        /// <summary>
        /// 应用程序虚拟目录。
        /// </summary>
        public static string ApplicationUrl { get; private set; }
        /// <summary>
        /// 获取物理路径。
        /// </summary>
        /// <param name="virtualPath">虚拟路径。</param>
        /// <returns>返回物理路径。</returns>
        public static string MapPath(string virtualPath)
        {
            if (string.IsNullOrEmpty(virtualPath))
                return ApplicationPath;
            virtualPath = virtualPath.Trim();
            string path = HostingEnvironment.MapPath(virtualPath);
            if (path != null)
            {
                return path;
            }
            Guard.IsNotVirtualPath(virtualPath, "virtualPath");
            if (!virtualPath.StartsWith("~/"))
                throw new ArgumentException(Resources.SourceMustBeAVirtualPathWhichShouldStartsWithTileAndSlash, "virtualPath");
            virtualPath = virtualPath.Substring(2);
            return Path.Combine(ApplicationPath, virtualPath);
        }
        /// <summary>
        /// 如果文件夹不存在则创建文件夹。
        /// </summary>
        /// <param name="path">文件夹的物理路径。</param>
        public static void CreateDirectory(string path)
        {
            if (!string.IsNullOrEmpty(path) && !Directory.Exists(path))
                Directory.CreateDirectory(path);
        }

        /// <summary>
        /// 获取临时文件夹文件的物理路径。
        /// </summary>
        /// <returns>返回新文件的物理路径。</returns>
        public static string GetNewTempPath()
        {
            string path = GetPath(Consts.TEMP_DIR_NAME);
            CreateDirectory(path);
            return Path.Combine(path, Guid.NewGuid().ToString());
        }

        /// <summary>
        /// 获取用户文件物理路径。
        /// </summary>
        /// <param name="args">路径参数。</param>
        /// <returns>返回用户文件物理路径。</returns>
        public static string GetUserFilePath(params object[] args)
        {
            string file = null;
            if (args != null)
                file = args.Join("/");
            file = GetPath(Consts.USER_FILES_DIR_NAME, file);
            CreateDirectory(Path.GetDirectoryName(file));
            return file;
        }

        /// <summary>
        /// 获取用户文件URL路径。
        /// </summary>
        /// <param name="args">路径参数。</param>
        /// <returns>返回用户文件URL路径。</returns>
        public static string GetUserFileUrl(params object[] args)
        {
            return GetUrl(Consts.USER_FILES_DIR_NAME, args);
        }

        /// <summary>
        /// 获取物理路径。
        /// </summary>
        /// <param name="args">路径参数。</param>
        /// <returns>返回物理路径。</returns>
        public static string GetPath(params object[] args)
        {
            string file = ApplicationPath;
            if (args != null)
            {
                string path = args.Join("/");
                if (path.StartsWith("~"))
                    path = path.Substring(2);
                file = Path.Combine(file, path);
            }
            file = file.Replace('/', '\\');
            return file;
        }

        /// <summary>
        /// 获取URL地址。
        /// </summary>
        /// <param name="args">路径参数。</param>
        /// <returns>返回URL地址。</returns>
        public static string GetUrl(params object[] args)
        {
            if (args == null)
                return ApplicationUrl;
            string path = args.Join("/");
            if (path.StartsWith("~"))
                path = path.Substring(2);
            return Path.Combine(ApplicationUrl, path);
        }

        /// <summary>
        /// 获取URL地址。
        /// </summary>
        /// <param name="args">路径参数。</param>
        /// <returns>返回URL地址。</returns>
        public static string GetUtil(params object[] args)
        {
            string file = ApplicationUrl + Consts.UTIL_DIR_NAME;
            if (args != null)
            {
                string path = args.Join("/");
                if (path.StartsWith("~"))
                    path = path.Substring(2);
                file = Path.Combine(file, path);
            }
            return file;
        }

        private static string backendName;
        /// <summary>
        /// 后台区域名称。
        /// </summary>
        public static string Backend {
            get {
                try
                {
                    backendName = WebConfigurationManager.AppSettings["Backend.Name"];
                    if(string.IsNullOrEmpty(backendName))
                        backendName = Consts.Backend;
                }
                catch { backendName = Consts.Backend; }
                return backendName;
            }
        }

        /// <summary>
        /// 获取后台目录。
        /// </summary>
        /// <param name="args">路径参数。</param>
        /// <returns>返回URL地址。</returns>
        public static string GetBackend(params object[] args)
        {
            return GetUrl(Backend, args.Join("/"));
        }
        #endregion

        #region xml
        /// <summary>
        /// 将XML字符串反序列化为对象。
        /// </summary>
        /// <typeparam name="T">当前类型。</typeparam>
        /// <param name="xml">XML字符串。</param>
        /// <returns>返回当前类型实例对象。</returns>
        public static T Deserialize<T>(string xml)
        {
            return (T)Deserialize(xml, typeof(T));
        }
        /// <summary>
        /// 将XML字符串反序列化为对象。
        /// </summary>
        /// <param name="xml">XML字符串。</param>
        /// <typeparam name="type">当前类型。</typeparam>
        /// <returns>返回当前类型实例对象。</returns>
        public static object Deserialize(string xml, Type type)
        {
            object obj = null;
            if (!string.IsNullOrEmpty(xml))
            {
                using (StringReader reader = new StringReader(xml))
                {
                    XmlSerializer serializer = new XmlSerializer(type);
                    try
                    {
                        obj = serializer.Deserialize(reader);
                    }
                    catch (InvalidOperationException)
                    {
                    }
                    reader.Close();
                }
            }
            return obj;
        }
        /// <summary>
        /// 将XML节点反序列化为对象。
        /// </summary>
        /// <typeparam name="T">当前类型。</typeparam>
        /// <param name="node">XML节点。</param>
        /// <returns>返回当前类型实例对象。</returns>
        public static T Deserialize<T>(XmlNode node)
        {
            return Deserialize<T>(node.OuterXml);
        }
        /// <summary>
        /// 将对象序列化为字符串。
        /// </summary>
        /// <param name="obj">要序列化的对象。</param>
        /// <returns>返回序列化后的字符串。</returns>
        public static string Serialize(object obj)
        {
            string str = null;
            if (obj != null)
            {
                XmlSerializer serializer = new XmlSerializer(obj.GetType());
                using (StringWriter writer = new StringWriter(CultureInfo.InvariantCulture))
                {
                    serializer.Serialize((TextWriter)writer, obj);
                    str = writer.ToString();
                    writer.Close();
                }
            }
            return str;
        }
        #endregion

        #region type
        /// <summary>
        /// 返回当前类型子类的对象集合。
        /// </summary>
        /// <param name="directoryName">查询DLL目录，默认(null)为bin</param>
        /// <param name="isAbstract">是否包含抽象类。</param>
        /// <returns>返回当前类型子类的对象集合。</returns>
        public static List<T> FindType<T>(string directoryName = null, bool isAbstract = false)
        {
            Type baseType = typeof(T);
            string baseDirectory = MapPath(directoryName ?? "~/bin");
            List<T> types = new List<T>();
            foreach (string file in Directory.GetFileSystemEntries(baseDirectory, "*.dll"))
            {
                try
                {
                    Assembly asm = Assembly.LoadFrom(file);
                    if (baseType.IsInterface)
                    {
                        foreach (Type t in asm.GetTypes())
                        {
                            if (!t.IsPublic || (!isAbstract && t.IsAbstract) || t.GetInterface(baseType.FullName) == null)
                                continue;
                            try
                            {
                                types.Add((T)asm.CreateInstance(t.FullName));
                            }
                            catch
                            {
                            }
                        }
                    }
                    else
                    {
                        foreach (Type t in asm.GetTypes())
                        {
                            if (!t.IsPublic || (!isAbstract && t.IsAbstract) || !t.IsSubclassOf(baseType))
                                continue;
                            try
                            {
                                types.Add((T)asm.CreateInstance(t.FullName));
                            }
                            catch
                            {
                            }
                        }
                    }
                }
                catch
                {
                }
            }
            return types;
        }
        /// <summary>
        /// 循环执行枚举中的所有值。
        /// </summary>
        /// <typeparam name="T">枚举类型，否则将抛出错误。</typeparam>
        /// <param name="action">执行枚举代理方法，参数为枚举当前值。</param>
        public static void ForEach<T>(Action<T> action)
        {
            Type type = typeof(T);
            if (type.IsEnum)
            {
                foreach (T value in Enum.GetValues(type))
                    action(value);
            }
            else
                throw new Exception(Resources.TypeIsNotEnum);
        }
        #endregion

        #region cryptors
        private static byte[] GetSymmetricAlgorithmSeed(string seed)
        {
            if(string.IsNullOrEmpty(seed))
            {
                seed = ConfigurationManager.AppSettings["SymmetricAlgorithmSeed"];
                if(string.IsNullOrEmpty(seed))
                    seed = Consts.DEFAULT_CRYPTOR_SEED;
            }
            var buffer = Cryptors.Make(seed, EncryptionAlgorithm.MD5);
            buffer = Cryptors.Make(seed, EncryptionAlgorithm.SHA256);
            buffer = Cryptors.Make(seed, EncryptionAlgorithm.SHA1);
            buffer = Cryptors.Make(seed, EncryptionAlgorithm.SHA512);
            return buffer;
        }
        /// <summary>
        /// 加密字符串。
        /// </summary>
        /// <param name="algorithm">加密方法。</param>
        /// <param name="value">用于加密的字符串。</param>
        /// <param name="seed">双向加密密钥。</param>
        /// <returns>返回加密后的字符串，如果加密方法为Clear直接返回字符串，否则返回加密后的64编码字符串。</returns>
        public static string Encrypt(EncryptionAlgorithm algorithm, string value, string seed = null)
        {
            if (algorithm == EncryptionAlgorithm.Clear)
                return value;
            if (algorithm <= EncryptionAlgorithm.SHA512)
                return Cryptors.Make(value, algorithm).ToBase64();
            var symmetric = new SymmetricAlgorithm(algorithm);
            var byteSeed = GetSymmetricAlgorithmSeed(seed);
            var key = new byte[symmetric.KeySize];
            Buffer.BlockCopy(byteSeed, 0, key, 0, symmetric.KeySize);
            var result = symmetric.Encrypt(Encoding.Default.GetBytes(value), key);
            var buffer = new byte[result.Length+symmetric.IVSize];
            Buffer.BlockCopy(result, 0, buffer, 0, result.Length);
            Buffer.BlockCopy(symmetric.IV, 0, buffer, result.Length, symmetric.IVSize);
            return buffer.ToBase64();
        }
        /// <summary>
        /// 解密字符串。
        /// </summary>
        /// <param name="algorithm">解密方法。</param>
        /// <param name="value">用于解密的字符串。</param>
        /// <param name="seed">双向加密密钥。</param>
        /// <returns>返回解密后的字符串，如果解密方法为Clear或者为单向加密的方法，则直接返回字符串，否则返回解密后的字符串。</returns>
        public static string Decrypt(EncryptionAlgorithm algorithm, string value, string seed = null)
        {
            if (algorithm <= EncryptionAlgorithm.SHA512)
                return value;
            var symmetric = new SymmetricAlgorithm(algorithm);
            var byteSeed = GetSymmetricAlgorithmSeed(seed);
            var key = new byte[symmetric.KeySize];
            Buffer.BlockCopy(byteSeed, 0, key, 0, symmetric.KeySize);
            var buffer = value.FromBase64();
            var iv = new byte[symmetric.IVSize];
            Buffer.BlockCopy(buffer, buffer.Length - symmetric.IVSize, iv, 0, symmetric.IVSize);
            symmetric.IV = iv;
            var data = new byte[buffer.Length - symmetric.IVSize];
            Buffer.BlockCopy(buffer, 0, data, 0, buffer.Length - symmetric.IVSize);
            return Encoding.Default.GetString(symmetric.Decrypt(data, key));
        }
        /// <summary>
        /// 计算文件MD5值。
        /// </summary>
        /// <param name="fileName">文件名。</param>
        /// <returns>返回MD5值。</returns>
        public static string HashFile(string fileName)
        {
            System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();
            using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
            {
                return BitConverter.ToString(md5.ComputeHash(fs)).Replace("-", string.Empty);
            }
        }
        #endregion

        #region images
        /// <summary>
        /// 获取图片的格式。
        /// </summary>
        /// <param name="image">图片实例对象。</param>
        /// <param name="extension">返回扩展名称（不带.）。</param>
        /// <returns>返回图片格式。</returns>
        public static ImageFormat GetFormat(Bitmap image, out string extension)
        {
            ImageFormat format;
            if (image.RawFormat.Guid == ImageFormat.Png.Guid || image.RawFormat.Guid == ImageFormat.Gif.Guid)
                format = ImageFormat.Png;
            else
                format = ImageFormat.Jpeg;
            extension = format.ToString().ToLower();
            if (extension == "jpeg")
            {
                extension = "jpg";
            }
            return format;
        }
        /// <summary>
        /// 将图片转换后保存。
        /// </summary>
        /// <param name="path">当前图片地址。</param>
        /// <param name="width">图片的长度。</param>
        /// <param name="height">图片的高度。</param>
        /// <param name="executeFileNameForParamExtension">获取保存后的文件名，参数为当前文件的扩展名，不包含“.”。</param>
        /// <param name="realSize">是否使用真实大小，即<paramref name="width"/>和<paramref name="height"/>指定的大小。</param>
        public static void SaveImage(string path, int width, int height, Func<string, string> executeFileNameForParamExtension, bool realSize = false)
        {
            if (!File.Exists(path))
                throw new Exception(string.Format("File “{0}” is not existed!", path));
            SaveImage(System.Drawing.Image.FromFile(path), width, height, executeFileNameForParamExtension, realSize);
        }
        /// <summary>
        /// 将图片转换后保存。
        /// </summary>
        /// <param name="img">当前图片实例对象。</param>
        /// <param name="width">图片的长度。</param>
        /// <param name="height">图片的高度。</param>
        /// <param name="executeFileNameForParamExtension">获取保存后的文件名，参数为当前文件的扩展名，不包含“.”。</param>
        /// <param name="realSize">是否使用真实大小，即<paramref name="width"/>和<paramref name="height"/>指定的大小。</param>
        public static void SaveImage(System.Drawing.Image img, int width, int height, Func<string, string> executeFileNameForParamExtension, bool realSize = false)
        {
            Bitmap image = new Bitmap(img);
            string extension;
            ImageFormat format = GetFormat(image, out extension);

            string path = executeFileNameForParamExtension(extension);
            CreateDirectory(Path.GetDirectoryName(path));
            Bitmap bitmap = image;
            if (image.Height > height || image.Width > width)
            {
                float rate = Math.Min((float)(((float)height) / ((float)image.Height)), (float)(((float)width) / ((float)image.Width)));
                int realWidth = realSize ? width : (int)(rate * image.Width);
                int realHeight = realSize ? height : (int)(rate * image.Height);
                bitmap = new Bitmap(realWidth, realHeight);
                using (Graphics graphics = Graphics.FromImage(bitmap))
                {
                    graphics.Clear(Color.Transparent);
                    graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    graphics.DrawImage(image, 0, 0, realWidth, realHeight);
                }
            }

            ImageCodecInfo encoder = null;
            foreach (ImageCodecInfo info in ImageCodecInfo.GetImageEncoders())
            {
                if (info.FormatID == format.Guid)
                {
                    encoder = info;
                    break;
                }
            }

            if (format.Guid == ImageFormat.Gif.Guid)
            {
                foreach (Color color in bitmap.Palette.Entries)
                {
                    if (color.A == 0)
                    {
                        bitmap.MakeTransparent(color);
                    }
                }
            }

            EncoderParameters encoderParams = new EncoderParameters(1);
            encoderParams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 0x55L);
            bitmap.Save(path, encoder, encoderParams);
            if (bitmap != image)
            {
                bitmap.Dispose();
            }
            image.Dispose();
        }
        #endregion

        #region Environment
        /// <summary>
        /// 获取当前系统信息。
        /// </summary>
        public static OperatingSystem OSVersion
        {
            get
            {
                return Environment.OSVersion;
            }
        }
        /// <summary>
        /// 获取机器名称。
        /// </summary>
        public static string MachineName
        {
            get
            {
                return Environment.MachineName;
            }
        }
        /// <summary>
        /// .NET版本信息。
        /// </summary>
        public static Version DotNetVersion
        {
            get
            {
                return Environment.Version;
            }
        }
        /// <summary>
        /// 获取当前工作物理路径。
        /// </summary>
        public static string WorkDirectory
        {
            get
            {
                return HostingEnvironment.ApplicationPhysicalPath;
            }
        }
        private static bool TryWriteWebConfig()
        {
            try
            {
                File.SetLastWriteTimeUtc(MapPath("~/web.config"), DateTime.Now);
                return true;
            }
            catch
            {
                return false;
            }
        }
        private static bool TryWriteBinFolder()
        {
            try
            {
                var binMarker = MapPath("~/bin/HostRestart");
                CreateDirectory(binMarker);

                using (var stream = File.CreateText(Path.Combine(binMarker, "marker.txt")))
                {
                    stream.WriteLine("Restart on '{0}'", DateTime.Now);
                    stream.Flush();
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
        /// <summary>
        /// 重启应用程序。
        /// </summary>
        public static void ResetApplication()
        {
            try
            {
                HttpRuntime.UnloadAppDomain();
            }
            catch {
                if (!TryWriteWebConfig()) TryWriteBinFolder();
            }
        }
        /// <summary>
        /// 获取当前类型的版本。
        /// </summary>
        /// <param name="type">类型。</param>
        /// <returns>返回当前类型版本。</returns>
        public static Version GetVersion(Type type)
        {
            return type.Assembly.GetName().Version;
        }
        #endregion

        #region resources
        /// <summary>
        /// 获取资源字符串。
        /// </summary>
        /// <param name="key">资源键。</param>
        /// <param name="classKey">全局资源类型名称，一般为文件名称的前一部分。</param>
        /// <returns>返回资源字符串。</returns>
        public static string GetGlobalResourceByKey(string key, string classKey = "Resource")
        {
            object resourceObj = HttpContext.GetGlobalResourceObject(classKey, key);

            if (resourceObj != null)
            {
                return resourceObj.ToString();
            }

            return null;
        }
        /// <summary>
        /// 当前线程区域。
        /// </summary>
        public static CultureInfo CurrentCulture
        {
            get
            {
                return Thread.CurrentThread.CurrentCulture;
            }
        }
        /// <summary>
        /// 获取或设置资源管理器使用的当前区域性以便在运行时查找区域性特定的资源。
        /// </summary>
        public static CultureInfo CurrentUICulture
        {
            get
            {
                return Thread.CurrentThread.CurrentUICulture;
            }
        }
        /// <summary>
        /// 设置区域性属性。
        /// </summary>
        /// <param name="resourceKey">资源键，默认为“zh-cn”。</param>
        public static void SetCulture(string resourceKey)
        {
            Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(resourceKey);
            Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(resourceKey);
        }
        /// <summary>
        /// 获取全局资源字符串。
        /// </summary>
        /// <param name="key">对象实例。</param>
        /// <param name="classKey">资源文件类，一般为资源文件前一部分名称。</param>
        /// <returns>返回全局资源字符串。</returns>
        public static string GetGlobalResourceByKey(object key, string classKey = "Resource")
        {
            var type = key.GetType();
            string resourceKey = key.ToString();
            if (type.IsEnum)
                resourceKey = type.Name + "_" + resourceKey;
            return Cores.GetGlobalResourceByKey(resourceKey, classKey);
        }
        #endregion
    }
}
